package com.snail.snailvr.utils;

import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Environment;
import android.util.Log;

import com.snail.snailvr.VRApp;
import com.snail.snailvr.event.BusProvider;
import com.snail.snailvr.event.LocalVideoRefreshComplete;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Stack;
import java.util.StringTokenizer;

import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;

/**
 * Created by shenc on 16-5-18.
 */
public class MediaLibrary {

    public static Observable<Boolean> scanMedia() {

        return Observable.create(new Observable.OnSubscribe<Boolean>() {
            @Override
            public void call(final Subscriber<? super Boolean> subscriber) {
                final Stack<File> directories = new Stack<File>();
                final HashSet<String> directoriesScanned = new HashSet<String>();

                List<File> mediaDirs = new ArrayList<>();

                if (mediaDirs.size() == 0) {

                    // Use all available storage directories as our default
                    String storageDirs[] = getMediaDirectories();
                    for (String dir : storageDirs) {
                        File f = new File(dir);
                        if (f.exists())
                            mediaDirs.add(f);
                    }

                }

                directories.addAll(mediaDirs);

                MediaItemFilter mediaFileFilter = new MediaItemFilter();

                ArrayList<File> mediaToScan = new ArrayList<File>();

                try {
                    // Count total files, and stack them
                    while (!directories.isEmpty()) {
                        File dir = directories.pop();
                        String dirPath = dir.getAbsolutePath();
                        File[] f = null;

                        // Skip some system folders
                        if (dirPath.startsWith("/proc/") || dirPath.startsWith("/sys/") || dirPath.startsWith("/dev/"))
                            continue;

                        // Do not scan again if same canonical path
                        try {
                            dirPath = dir.getCanonicalPath();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        if (directoriesScanned.contains(dirPath))
                            continue;
                        else
                            directoriesScanned.add(dirPath);

                        // Do not scan media in .nomedia folders
                        if (new File(dirPath + "/.nomedia").exists()) {
                            continue;
                        }

                        // Filter the extensions and the folders
                        try {
                            if ((f = dir.listFiles(mediaFileFilter)) != null) {
                                for (File file : f) {
                                    if (file.isFile()) {
                                        mediaToScan.add(file);
                                    } else if (file.isDirectory()) {
                                        directories.push(file);
                                    }
                                }
                            }
                        } catch (Exception e) {
                            // listFiles can fail in OutOfMemoryError, go to the next folder
                            continue;
                        }

                    }
                    if(mediaToScan.size()>0) {
                        final String lastFilePath = mediaToScan.get(mediaToScan.size()-1).getAbsolutePath();

                        for (File file : mediaToScan) {
                            MediaScannerConnection.scanFile(VRApp.getInstance(),
                                    new String[]{file.getAbsolutePath()}, null,
                                    new MediaScannerConnection.OnScanCompletedListener() {
                                        public void onScanCompleted(String path, Uri uri) {
                                            if (path.equals(lastFilePath))
                                                BusProvider.getInstance().post(new LocalVideoRefreshComplete());
                                        }
                                    });
                        }
                    }

                } catch (Exception e) {
                    LogUtils.d("exception="+e.toString());
                } finally {
                }
            }
        }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
    }

    /**
     * Filters all irrelevant files
     */
    private static class MediaItemFilter implements FileFilter {

        @Override
        public boolean accept(File f) {
            boolean accepted = false;

            if (!f.isHidden()) {
                if (f.isDirectory() && !FOLDER_BLACKLIST.contains(f.getPath().toLowerCase(Locale.ENGLISH))) {
                    accepted = true;
                } else {
                    String fileName = f.getName().toLowerCase(Locale.ENGLISH);
                    int dotIndex = fileName.lastIndexOf(".");
                    if (dotIndex != -1) {

                        String fileExt = fileName.substring(dotIndex);
                        Log.d("TAG", fileExt);
                        accepted = fileExt.endsWith(".mp4") || fileExt.endsWith(".flv");
                    }
                }
            }
            return accepted;
        }
    }

    public final static HashSet<String> FOLDER_BLACKLIST;

    static {
        final String[] folder_blacklist = {
                "/alarms",
                "/notifications",
                "/ringtones",
                "/media/alarms",
                "/media/notifications",
                "/media/ringtones",
                "/media/audio/alarms",
                "/media/audio/notifications",
                "/media/audio/ringtones",
                "/Android/data/"};

        FOLDER_BLACKLIST = new HashSet<>();
        for (String item : folder_blacklist)
            FOLDER_BLACKLIST.add(android.os.Environment.getExternalStorageDirectory().getPath() + item);
    }


    public static ArrayList<String> getStorageDirectories() {
        BufferedReader bufReader = null;
        ArrayList<String> list = new ArrayList<String>();
        list.add(Environment.getExternalStorageDirectory().getPath());

        List<String> typeWL = Arrays.asList("vfat", "exfat", "sdcardfs", "fuse", "ntfs", "fat32", "ext3", "ext4");
        List<String> typeBL = Arrays.asList("tmpfs");
        String[] mountWL = {"/mnt", "/Removable"};
        String[] mountBL = {
                "/mnt/secure",
                "/mnt/shell",
                "/mnt/asec",
                "/mnt/obb",
                "/mnt/media_rw/extSdCard",
                "/mnt/media_rw/sdcard",
                "/storage/emulated"};
        String[] deviceWL = {
                "/dev/block/vold",
                "/dev/fuse",
                "/mnt/media_rw/extSdCard"};

        try {
            bufReader = new BufferedReader(new FileReader("/proc/mounts"));
            String line;
            while ((line = bufReader.readLine()) != null) {

                StringTokenizer tokens = new StringTokenizer(line, " ");

                String device = tokens.nextToken();
                String mountpoint = tokens.nextToken();
                String type = tokens.nextToken();

                // skip if already in list or if type/mountpoint is blacklisted
                if (list.contains(mountpoint) || typeBL.contains(type) || StartsWith(mountBL, mountpoint))
                    continue;

                // check that device is in whitelist, and either type or mountpoint is in a whitelist
                if (StartsWith(deviceWL, device) && (typeWL.contains(type) || StartsWith(mountWL, mountpoint)))
                    list.add(mountpoint);
            }
        } catch (FileNotFoundException e) {
        } catch (IOException e) {
        } finally {
            if (bufReader != null) {
                try {
                    bufReader.close();
                } catch (IOException e) {
                }
            }
        }
        return list;
    }

    public static String[] getMediaDirectories() {
        ArrayList<String> list = new ArrayList<String>();
        list.addAll(getStorageDirectories());
        //list.addAll(Arrays.asList(CustomDirectories.getCustomDirectories()));
        return list.toArray(new String[list.size()]);
    }

    static boolean StartsWith(String[] array, String text) {
        for (String item : array)
            if (text.startsWith(item))
                return true;
        return false;
    }
}
